/* TimeAlready - A Google Wave stopwatch and eggtimer gadget
 *
 * Copyright (C)2010 Christoph Budzinski <HaiColon at gmail dot com>
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Project hosting:
 *   http://code.google.com/p/timealready/
 */

// used for blinking the delimiter colons
var displayColons = true;

// stores the alarm time for the eggtimer
var later = new Date();

// stores the current time for the stopwatch
var earlier = null;

// stores the difference in milliseconds between the remote server
//   and the user's time
var remoteDiff = 0;

// this is a bugfix for Wave sending gadget changes two times
var lastActionSentAt = "";

// setInterval(myFunction, myTimeOut) IDs
var blinkTimerId = null;
var displayTimerId = null;

/*
 * Gets called when the gadget has finished loading
 * in a wave
 */
function initWave() {
	// call the normal init function
	init();
	
	// get time from a remote server to calculate time differences
	//   between users
	getRemoteTime();
	
	// set Google Wave callback for state changes
	wave.setStateCallback(gadgetStateChanged);
}

/*
 * Called when the gadget state object has changed
 */
function gadgetStateChanged() {
	var waveState = wave.getState();
	
	// this if is a bugfix for Wave sending gadget changes two times
	if(waveState.get('actionSentAt') != lastActionSentAt) {
		var actionType = waveState.get('actionType');
		if(actionType == "eggtimer") {
			later = new Date(parseInt(waveState.get('later')));
			setTimer();
		} else if(actionType == "stopwatchStart") {
			var starttime = new Date(parseInt(waveState.get('earlier')));
			toggleStopwatch(starttime);
		} else if(actionType == "stopwatchStop") {
			var starttime = new Date(parseInt(waveState.get('earlier')));
			var stoptime = new Date(parseInt(waveState.get('stoptime')));
			el("stopwatchButton").innerHTML = "Stop";
			el("thetimer").style["display"] = "block";
			toggleStopwatch(starttime, stoptime);
		}
		
		lastActionSentAt = waveState.get('actionSentAt');
	}
}

/*
 * Sends user's eggtimer target time to Wave
 *
 * If this succeeds, the gadgetStateChanged function calls the
 * actual setTimer function that sets the eggtimer
 */
function setTimerWave() {
	var fromtime = null;
	var time = null;
	if(time = parseSeconds(el('fromtime').value)) {
		fromtime = new Date();
		fromtime.setSeconds(fromtime.getSeconds() + time);
	} else {
		alert("I did not understand you, please verify the time you entered.");
		return null;
	}
	wave.getState().submitDelta({"later" : "" + (fromtime.getTime() + remoteDiff),
								"actionType" : "eggtimer",
								"actionSentAt" : "" + new Date().getTime()});
}

/*
 * Sends user's stopwatch start time to Wave
 *
 * If this succeeds, the gadgetStateChanged function calls the
 * actual toggleStopwatch function that sets the stopwatch
 */
function toggleStopwatchWave() {
	if(el("stopwatchButton").innerHTML != "Stop") {
		var starttime = new Date();
		wave.getState().submitDelta({"earlier" : "" + (starttime.getTime() + remoteDiff),
									"actionType" : "stopwatchStart",
									"actionSentAt" : "" + new Date().getTime()});
	} else {
		var stoptime = new Date();
		wave.getState().submitDelta({"stoptime" : "" + (stoptime.getTime() + remoteDiff),
									"actionType" : "stopwatchStop",
									"actionSentAt" : "" + new Date().getTime()});
	}
}

/*
 * Grabs the current time in milliseconds from a remote server
 *
 * Based on this idea:
 * http://groups.google.com/group/google-wave-api/msg/7c2f3000c1cd2090
 */
function getRemoteTime() {
	var params = {};
	params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
	
	gadgets.io.makeRequest("http://colongadgets.appspot.com/time?ts=" + new Date().getTime(), function(timeDiff) {
		var remoteTime = parseFloat(timeDiff.text) * 1000; // *1000 since it returns seconds not milliseconds
		var localTime = new Date().getTime();
		
		// calculate the difference between the server and the user's time
		remoteDiff = remoteTime - localTime;
	}, params);
}

/*
 * Gets called when the document has finished loading
 */
function init() {
	// blinking delimiter colons
	blinkTimerId = setInterval(function() {
		if(displayColons) {
			displayColons = false;
		} else {
			displayColons = true;
		}
	}, 500);
}

/*
 * Short alias function for document.getElementById(someElement)
 */
function el(element) {
	return document.getElementById(element);
}

/*
 * Toggles display of the settings div
 */
function toggleSettings() {
	if(el('settings').style['display'] == "block") {
		el('settings').style['display'] = 'none';
	} else {
		el('settings').style['display'] = "block";
	}
}

/*
 * Starts or stops the stopwatch
 */
function toggleStopwatch(starttime, stoptime) {
	if(el("stopwatchButton").innerHTML != "Stop") {
		var today = new Date();
		earlier = starttime;
		
		el("stopwatchButton").innerHTML = "Stop";
		el("thetimer").style["display"] = "block";
		
		if(displayTimerId) {
			clearInterval(displayTimerId);
			displayTimerId = null;
		}
		
		displayTimer(today, starttime);
		displayTimerId = setInterval(function() {
			today=new Date();
			displayTimer(today, starttime);
		}, 500);
	} else {
		if(displayTimerId) {
			clearInterval(displayTimerId);
			displayTimerId = null;
		}
		if(!stoptime) {
			stoptime = new Date();
		}
		if(earlier) {
			starttime = earlier;
			earlier = null;
		}
		displayTimer(stoptime, starttime);
		el("stopwatchButton").innerHTML = "Stopwatch";
	}
}

/*
 * Takes a string with an english language representation of a duration
 * and converts it into seconds
 *
 * returns the duration in seconds or null if the syntax was wrong
 */
function parseSeconds(text) {
	text = text.split("and ").join(""); // gsub
	var tokens = text.split(" ");
	
	if(tokens.length < 2) {
		tokens = tokens[0].split(""); // splits on each character
	}
	
	var time = 0;
	var number = null;
	var modifier = null;
	var numberPattern = /[0-9]+/i;
	var modifierPattern = /[a-zA-Z]+/i;
	
	for(token in tokens) {
		if(number == null) {
			if(numberPattern.exec(tokens[token])) {
				number = parseInt(tokens[token]);
			} else {
				return null; // abort because of syntax error
			}
		} else {
			if(modifier = modifierPattern.exec(tokens[token])) {
				if(modifier == "m" || modifier == "min" || modifier == "mins" || modifier == "minute" || modifier == "minutes") {
					time += number * 60;
				} else if(modifier == "h" || modifier == "hour" || modifier == "hours") {
					time += number * 60 * 60;
				} else if(modifier == "s" || modifier == "sec" || modifier == "secs" || modifier == "second" || modifier == "seconds") {
					time += number;
				} else {
					return null; // abort because of syntax error
				}
				number = null;
			} else {
				return null; // abort because of syntax error
			}
		}
	}
	return time;
}

/*
 * Starts the eggtimer with the value entered in the textfield
 */
function setTimer(fromtime) {
	if(fromtime) {
		var time = null;
		if(time = parseSeconds(fromtime)) {
			later = new Date();
			later.setSeconds(later.getSeconds() + time);
		} else {
			alert("I did not understand you, please verify the time you entered.");
			return null;
		}
	}
	
	el("settings").style["display"] = "none";
	el("thetimer").style["display"] = "block";
	
	el("stopwatchButton").innerHTML = "Stopwatch";
	
	if(displayTimerId) {
		clearInterval(displayTimerId);
		displayTimerId = null;
	}
	
	var today=new Date();
	displayTimer(later, today);
	displayTimerId = setInterval(function() {
		if(later.getTime() - today.getTime() + remoteDiff > 0) {
			today = new Date(new Date().getTime() + remoteDiff);
			displayTimer(later, today);
		} else {
			el('thetimer').innerHTML= "Time's up!";
		}
	}, 500);
}

/*
 * Displays the stopwatch or eggtimer
 */
function displayTimer(bigger, smaller)
{
	var countdown = new Date(bigger.getTime() - smaller.getTime());
	
	var h = countdown.getUTCHours();
	var m = countdown.getUTCMinutes();
	var s = countdown.getUTCSeconds();
	
	m = padTime(m);
	s = padTime(s);
	
	if(displayColons) {
		el('thetimer').innerHTML = formatTime(h, m, s, true);
	} else {
		el('thetimer').innerHTML = formatTime(h, m, s, false);
	}
}

/*
 * Takes hours, minutes and seconds and turns it into a
 * formatted string for display in the timer div
 *
 * Also takes a boolean colons argument that displays the
 * delimiter colons if true
 */
function formatTime(hours, minutes, seconds, colons) {
	if(colons) {
		fString = hours + "<span>h</span>:";
		fString += minutes + "<span>m</span>:";
		fString += seconds + "<span>s</span>";
		return fString;
	} else {
		fString = hours + "<span>h</span> ";
		fString += minutes + "<span>m</span> ";
		fString += seconds + "<span>s</span>";
		return fString;
	}
}

/*
 * Prepends a zero to a number < 10
 */
function padTime(i) {
	if (i < 10) {
		i = "0" + i;
	}
	
	return i;
}